package com.learning.thread.juc.tool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * ClassName: SemaphoreFirst
 * Description: 信号量 Semaphore 。可以维护当前访问自身的线程个数，并提供了同步机制。功能类似于 Executors.newWorkStealingPool()
 * <p>
 * <p>
 * 1、使用 Semaphore 可以控制同时访问资源的线程个数。
 * 相当于蹲坑，一共有3个坑，10个人来，只能三个三个上，三个并发。例如，实现一个文件允许的并发访问数。
 * <p>
 * 2、单个Semaphore，可以实现线程互斥锁的功能。并且可以由一个线程得到‘锁’，再由另一个线程释放‘锁’。
 * 这可应用于死锁回复的一些场合。如一个人上厕所，晕在里面了，本来只有里面那个人才能释放。现在可以由其他人释放。
 * <p>
 * Date: 2016/11/26 17:45
 *
 * @author SAM SHO
 * @version V1.0
 */
public class SemaphoreFirst {

    public static void main(String[] args) {
        SemaphoreFirst semaphoreFirst = new SemaphoreFirst();
//        semaphoreFirst.test1();
        semaphoreFirst.test2();
    }

    void test1() {
        ObjectData data = new ObjectData();
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.execute(new MyThread(data));
        executor.execute(new MyThread(data));
        executor.execute(new MyThread(data));
    }


    class MyThread implements Runnable {

        private ObjectData data;

        public MyThread(ObjectData data) {
            this.data = data;
        }

        @Override
        public void run() {
            try {
                data.get();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    class ObjectData {
        public static final int MAX = 1;

        /**
         * 只允许1个线程并发访问
         */
        final Semaphore sp = new Semaphore(MAX);

        void get() throws Exception {
            // 获取信号灯
            sp.acquire();

            // sp.availablePermits() 被拿走的灯的个数
            System.out.println("线程 " + Thread.currentThread().getName() + " 进入，当前已有" + (MAX - sp.availablePermits()) + "线程");
            Thread.sleep((long) (Math.random() * 10000));

            System.out.println("线程 " + Thread.currentThread().getName() + " 即将离开");

            // 释放获取的灯
            sp.release();
            System.out.println("线程 " + Thread.currentThread().getName() + " 已离开，当前已有" + (MAX - sp.availablePermits()) + "线程");
        }

    }


    void test2() {
        // 创建线程池，newCachedThreadPool,在没有空闲的情况下，有多少任务，创建多少线程
        ExecutorService service = Executors.newCachedThreadPool();

        // 建立信号灯,有参数fair，实现线程进入优先级。true的时候，先到先进
        final Semaphore sp = new Semaphore(3);

        //创建10个线程
        for (int i = 0; i < 5; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        sp.acquire();//获取信号灯
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }


                    //sp.availablePermits() 被拿走的灯的个数
                    System.out.println("线程" + Thread.currentThread().getName() +
                            "进入，当前已有" + (3 - sp.availablePermits()) + "线程");
                    try {
                        Thread.sleep((long) (Math.random() * 10000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程" + Thread.currentThread().getName() +
                            "即将离开");
                    sp.release();//释放获取的灯
                    //下面代码有时候执行不准确
                    System.out.println("线程" + Thread.currentThread().getName() +
                            "已离开，当前已有" + (3 - sp.availablePermits()) + "线程");
                }
            };
            //执行10个线程
            service.execute(runnable);
        }
    }

    /*
线程pool-1-thread-1进入，当前已有1线程
线程pool-1-thread-2进入，当前已有2线程
线程pool-1-thread-3进入，当前已有3线程
线程pool-1-thread-1即将离开
线程pool-1-thread-4进入，当前已有3线程
线程pool-1-thread-1已离开，当前已有3线程
线程pool-1-thread-2即将离开
线程pool-1-thread-5进入，当前已有3线程
线程pool-1-thread-2已离开，当前已有3线程
线程pool-1-thread-4即将离开
线程pool-1-thread-4已离开，当前已有2线程
线程pool-1-thread-5即将离开
线程pool-1-thread-5已离开，当前已有1线程
线程pool-1-thread-3即将离开
线程pool-1-thread-3已离开，当前已有0线程

    */


}
